use crate::dto;
use crate::service::Error;
use crate::service::{
    endpoint::EndpointService, endpoint_peer::EndpointPeerService,
    endpoint_peer_wireguard_config_manager::EndpointPeerWireguardConfigManagerService,
};
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

#[async_trait::async_trait]
pub trait EndpointPeerManagerService: EndpointPeerService {
    async fn list_info(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::endpoint_peer::EndpointPeerInfo>, Error>;

    async fn all_info(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
    ) -> Result<Vec<dto::endpoint_peer::EndpointPeerInfo>, Error>;

    async fn get_info(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::endpoint_peer::EndpointPeerInfo, Error>;

    async fn try_get_info(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::endpoint_peer::EndpointPeerInfo>, Error>;

    async fn get_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint::EndpointItem, Error>;

    async fn try_get_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint::EndpointItem>, Error>;

    async fn get_peer_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint::EndpointItem, Error>;

    async fn try_get_peer_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint::EndpointItem>, Error>;
}

pub struct DefaultEndpointPeerManagerService {
    endpoint_service: Arc<dyn EndpointService>,
    endpoint_peer_service: Arc<dyn EndpointPeerService>,
    endpoint_peer_wireguard_config_manager_service:
        Arc<dyn EndpointPeerWireguardConfigManagerService>,
}

impl DefaultEndpointPeerManagerService {
    pub fn new(
        endpoint_service: Arc<dyn EndpointService>,
        endpoint_peer_service: Arc<dyn EndpointPeerService>,
        endpoint_peer_wireguard_config_manager_service: Arc<
            dyn EndpointPeerWireguardConfigManagerService,
        >,
    ) -> Self {
        Self {
            endpoint_service,
            endpoint_peer_service,
            endpoint_peer_wireguard_config_manager_service,
        }
    }

    async fn list_config_by_endpoint_peers(
        &self,
        endpoint_peers: Vec<dto::core::endpoint_peer::EndpointPeerItem>,
    ) -> Result<Vec<dto::endpoint_peer::EndpointPeerInfo>, Error> {
        let mut peer_config_list = Vec::new();
        for peer in endpoint_peers.iter() {
            let peer_config = self.get_info(peer.endpoint_peer_id.as_str()).await?;

            peer_config_list.push(peer_config);
        }
        Ok(peer_config_list)
    }
}

#[async_trait::async_trait]
impl EndpointPeerService for DefaultEndpointPeerManagerService {
    async fn add(
        &self,
        params: &dto::core::endpoint_peer::AddParams,
    ) -> Result<dto::core::endpoint_peer::AddResponseData, Error> {
        self.endpoint_peer_service.add(params).await
    }

    async fn list(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        self.endpoint_peer_service.list(filter, offset, limit).await
    }

    async fn all(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
    ) -> Result<Vec<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        self.endpoint_peer_service.all(filter).await
    }

    async fn get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint_peer::EndpointPeerItem, Error> {
        self.endpoint_peer_service.get(endpoint_peer_id).await
    }

    async fn try_get(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint_peer::EndpointPeerItem>, Error> {
        self.endpoint_peer_service.try_get(endpoint_peer_id).await
    }

    async fn delete(&self, endpoint_peer_id: &str) -> Result<(), Error> {
        self.endpoint_peer_wireguard_config_manager_service
            .delete(endpoint_peer_id)
            .await?;
        self.endpoint_peer_service.delete(endpoint_peer_id).await
    }

    async fn count(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<u64, Error> {
        self.endpoint_peer_service.count(filter).await
    }

    async fn exists(&self, filter: &dto::core::endpoint_peer::Filter) -> Result<bool, Error> {
        self.endpoint_peer_service.exists(filter).await
    }
}

#[async_trait::async_trait]
impl EndpointPeerManagerService for DefaultEndpointPeerManagerService {
    async fn list_info(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
        offset: u64,
        limit: u64,
    ) -> Result<Vec<dto::endpoint_peer::EndpointPeerInfo>, Error> {
        let peers = self.list(filter, offset, limit).await?;

        let list = self.list_config_by_endpoint_peers(peers).await?;

        Ok(list)
    }

    async fn all_info(
        &self,
        filter: &dto::core::endpoint_peer::Filter,
    ) -> Result<Vec<dto::endpoint_peer::EndpointPeerInfo>, Error> {
        if filter == &Default::default() {
            return Err(Error::ResponseError(
                ResponseError::filter_can_not_be_empty(),
            ));
        }

        let peers = self.all(filter).await?;

        let list = self.list_config_by_endpoint_peers(peers).await?;

        Ok(list)
    }

    async fn get_info(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::endpoint_peer::EndpointPeerInfo, Error> {
        let config = self
            .try_get_info(endpoint_peer_id)
            .await?
            .ok_or(ResponseError::endpoint_peer_not_found(endpoint_peer_id))?;

        Ok(config)
    }
    async fn try_get_info(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::endpoint_peer::EndpointPeerInfo>, Error> {
        let peer = self.get(endpoint_peer_id).await?;

        let peer_endpoint = self
            .endpoint_service
            .get(peer.endpoint_peer_id.as_str())
            .await?;

        let peer_wireguard_config = self
            .endpoint_peer_wireguard_config_manager_service
            .try_get_config(endpoint_peer_id)
            .await?;

        Ok(Some(dto::endpoint_peer::EndpointPeerInfo {
            inner: peer,
            inner_endpoint: peer_endpoint.inner,
            wireguard: peer_wireguard_config,
        }))
    }

    async fn get_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint::EndpointItem, Error> {
        let peer = self.endpoint_peer_service.get(endpoint_peer_id).await?;

        self.endpoint_service
            .get(peer.inner.endpoint_id.as_str())
            .await
    }

    async fn try_get_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint::EndpointItem>, Error> {
        let peer = match self.endpoint_peer_service.try_get(endpoint_peer_id).await? {
            None => return Ok(None),
            Some(v) => v,
        };

        self.endpoint_service
            .try_get(peer.inner.endpoint_id.as_str())
            .await
    }

    async fn get_peer_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<dto::core::endpoint::EndpointItem, Error> {
        let peer = self.endpoint_peer_service.get(endpoint_peer_id).await?;

        self.endpoint_service
            .get(peer.inner.peer_endpoint_id.as_str())
            .await
    }

    async fn try_get_peer_endpoint(
        &self,
        endpoint_peer_id: &str,
    ) -> Result<Option<dto::core::endpoint::EndpointItem>, Error> {
        let peer = match self.endpoint_peer_service.try_get(endpoint_peer_id).await? {
            None => return Ok(None),
            Some(v) => v,
        };

        self.endpoint_service
            .try_get(peer.inner.peer_endpoint_id.as_str())
            .await
    }
}
